package fr.asip.cps3.exemple.modele.objets;

import iaik.pkcs.pkcs11.wrapper.CK_MECHANISM_INFO;
import iaik.pkcs.pkcs11.wrapper.PKCS11;
import iaik.pkcs.pkcs11.wrapper.PKCS11Constants;
import iaik.pkcs.pkcs11.wrapper.PKCS11Exception;

import org.apache.log4j.Logger;

import fr.asip.cps3.exemple.modele.exceptions.ExceptionObjet;
import fr.asip.cps3.exemple.modele.exceptions.ExceptionProgrammeExemple;
import fr.asip.cps3.exemple.modele.traitements.util.TraitementsUtil;

/**
 * Classe modélisant un algorithme
 */
public class Algo {

	/**
	 * Le loggeur
	 */
	 private static Logger log = Logger.getLogger(Algo.class);	
	
	/**
	 * Identifiant du slot hébergant le token
	 */
	private long idSlotToken;	
	
	/**
	 * Identifiant de l'algorithme
	 */
	private long idAlgo;

	/**
	 * Informations sur l'algorithme
	 */
	private CK_MECHANISM_INFO info;

	/**
	 * Constructeur
	 * @param librairie Librairie PKCS#11 à utiliser
	 * @param slotToken Identifiant du slot hébergant le token
	 * @param algo Identifiant de l'algorithme
	 * @throws ExceptionObjet 
	 */
	public Algo(PKCS11 librairie, long idSlotToken, long idAlgo) throws ExceptionObjet {

		this.idSlotToken = idSlotToken;
		this.idAlgo = idAlgo;
		
		// Récupération des informations concernant l'algorithme
		chargeInfo(librairie);
			
	}

	/**
	 * (Re)chargement explicite des informations de l'algorithme
	 * @param librairie Librairie PKCS#11 à utiliser
	 * @throws ExceptionObjet 
	 */
	public void chargeInfo(PKCS11 librairie) throws ExceptionObjet{
		
		if(librairie != null) {		
		
			// On interroge la carte pour récupérer les informations de l'algorithme
			try {
				
				info = librairie.C_GetMechanismInfo(idSlotToken, idAlgo);
				
			} catch (PKCS11Exception e) {

				// Si une erreur PKCS#11 est rencontrée on log et on lève l'exception
				log.error("Une erreur est survenue lors de la recuperation des informations de l'algorithme : "+TraitementsUtil.retranscritCodeRetour(e.getErrorCode()));
				throw new ExceptionObjet(ExceptionProgrammeExemple.TYPE_OBJET_ALGORITHME, "Une erreur est survenue lors de la récuperation des informations de l'algorithme");
			}
		
		} else {
			
			// Si la librairie est nulle il est impossible de récupérer les informations de l'algorithme
			log.error("La librairie est nulle");
			throw new ExceptionObjet(ExceptionProgrammeExemple.TYPE_OBJET_ALGORITHME, "La librairie est nulle");
			
		}		
	}
	
	/**
	 * Renvoire le libellée de l'algorithme
	 * @return Libellé de l'algorithme
	 */
	public String getLibelle() {
		
		return TraitementsUtil.getNomAlgo(idAlgo).replaceAll("_", " ").replaceAll("CKM", "").trim();
		
	}

	/**
	 * Accesseur
	 * @return Id de l'algorithme
	 */
	public long getIdAlgo() {
		
		return idAlgo;
		
	}

	/**
	 * Accesseur
	 * @return Identifiant du slot hébergant le token
	 */
	public long getIdSlotToken() {
		
		return idSlotToken;
		
	}
	
	/**
	 * Renvoie la taille minimale de la clé pour l'algorithme (l'unité de grandeur (bits ou octets) dépend du type d'algorithme)
	 * @return Taille minimale de la clé pour l'algorithme (l'unité de grandeur (bits ou octets) dépend du type d'algorithme)
	 * @throws ExceptionObjet 
	 */
	public long getTailleMinCle() throws ExceptionObjet {
		
		if(info != null)
			return info.ulMinKeySize;
		else {
			
			log.error("Les informations de l'algorithme sont nulles, la taille minimale de la clé est indisponible");
			throw new ExceptionObjet(ExceptionProgrammeExemple.TYPE_OBJET_ALGORITHME, "La taille minimale de la clé est indisponible");
			
		}
		
	}
	
	/**
	 * Renvoie la taille maximale de la clé pour l'algorithme (l'unité de grandeur (bits ou octets) dépend du type d'algorithme)
	 * @return Taille maximale de la clé pour l'algorithme (l'unité de grandeur (bits ou octets) dépend du type d'algorithme)
	 * @throws ExceptionObjet 
	 */
	public long getTailleMaxCle() throws ExceptionObjet {
		
		if(info != null)
			return info.ulMaxKeySize;
		else {
			
			log.error("Les informations de l'algorithme sont nulles, la taille maximale de la clé est indisponible");
			throw new ExceptionObjet(ExceptionProgrammeExemple.TYPE_OBJET_ALGORITHME, "La taille maximale de la clé est indisponible");
			
		}
		
	}

	/**
	 * L'algorithme est-il directement appliqué sur la carte?
	 * @return Réponse
	 * @throws ExceptionObjet 
	 */
	public boolean isHardware() throws ExceptionObjet {
		
		if(info != null)
			return (info.flags & PKCS11Constants.CKF_HW)!=0?true:false;
		else {
			
			log.error("Les informations de l'algorithme sont nulles, l'information sur l'algorithme est indisponible");
			throw new ExceptionObjet(ExceptionProgrammeExemple.TYPE_OBJET_ALGORITHME, "Linformation sur l'algorithme est indisponible");
			
		}
		
	}

	/**
	 * L'algorithme peut-il être utilisé pour le chiffrement?
	 * @return Réponse
	 * @throws ExceptionObjet 
	 */
	public boolean isChiffrement() throws ExceptionObjet {
		
		if(info != null)
			return (info.flags & PKCS11Constants.CKF_ENCRYPT)!=0?true:false;
		else {
			
			log.error("Les informations de l'algorithme sont nulles, l'information sur l'algorithme est indisponible");
			throw new ExceptionObjet(ExceptionProgrammeExemple.TYPE_OBJET_ALGORITHME, "Linformation sur l'algorithme est indisponible");
			
		}

		
	}
	
	/**
	 * L'algorithme peut-il être utilisé pour le déchiffrement?
	 * @return Réponse
	 * @throws ExceptionObjet 
	 */
	public boolean isDechiffrement() throws ExceptionObjet {
		
		if(info != null)
			return (info.flags & PKCS11Constants.CKF_DECRYPT)!=0?true:false;
		else {
			
			log.error("Les informations de l'algorithme sont nulles, l'information sur l'algorithme est indisponible");
			throw new ExceptionObjet(ExceptionProgrammeExemple.TYPE_OBJET_ALGORITHME, "Linformation sur l'algorithme est indisponible");
			
		}

		
	}
	
	/**
	 * L'algorithme peut-il être utilisé pour réaliser un condensat?
	 * @return Réponse
	 * @throws ExceptionObjet 
	 */
	public boolean isRealiseCondensat() throws ExceptionObjet {
		
		if(info != null)
			return (info.flags & PKCS11Constants.CKF_DIGEST)!=0?true:false;
		else {
			
			log.error("Les informations de l'algorithme sont nulles, l'information sur l'algorithme est indisponible");
			throw new ExceptionObjet(ExceptionProgrammeExemple.TYPE_OBJET_ALGORITHME, "Linformation sur l'algorithme est indisponible");
			
		}

		
	}
	
	/**
	 * L'algorithme peut-il être utilisé pour réaliser une signature?
	 * @return Réponse
	 * @throws ExceptionObjet 
	 */
	public boolean isRealiseSignature() throws ExceptionObjet {
		
		if(info != null)
			return (info.flags & PKCS11Constants.CKF_SIGN)!=0?true:false;
		else {
			
			log.error("Les informations de l'algorithme sont nulles, l'information sur l'algorithme est indisponible");
			throw new ExceptionObjet(ExceptionProgrammeExemple.TYPE_OBJET_ALGORITHME, "Linformation sur l'algorithme est indisponible");
			
		}

		
	}
	
	/**
	 * L'algorithme peut-il être utilisé pour récupérer une donnée depuis une signature?
	 * @return Réponse
	 * @throws ExceptionObjet 
	 */
	public boolean isRecupereSignature() throws ExceptionObjet {
		
		if(info != null)
			return (info.flags & PKCS11Constants.CKF_SIGN_RECOVER)!=0?true:false;
		else {
			
			log.error("Les informations de l'algorithme sont nulles, l'information sur l'algorithme est indisponible");
			throw new ExceptionObjet(ExceptionProgrammeExemple.TYPE_OBJET_ALGORITHME, "Linformation sur l'algorithme est indisponible");
			
		}

		
	}
	
	/**
	 * L'algorithme peut-il être utilisé pour réaliser la vérification d’une signature ou d’un MAC (Message Authentication Code)?
	 * @return Réponse
	 * @throws ExceptionObjet 
	 */
	public boolean isVerificationSignature() throws ExceptionObjet {
		
		if(info != null)
			return (info.flags & PKCS11Constants.CKF_VERIFY)!=0?true:false;
		else {
			
			log.error("Les informations de l'algorithme sont nulles, l'information sur l'algorithme est indisponible");
			throw new ExceptionObjet(ExceptionProgrammeExemple.TYPE_OBJET_ALGORITHME, "Linformation sur l'algorithme est indisponible");
			
		}

	}
	
	/**
	 * L'algorithme peut-il être utilisé pour réaliser la vérification d’une signature ou d’un MAC (Message Authentication Code) avec récupération de la donnée depuis la signature?
	 * @return Réponse
	 * @throws ExceptionObjet 
	 */
	public boolean isVerificationParRecuperation() throws ExceptionObjet {
		
		if(info != null)
			return (info.flags & PKCS11Constants.CKF_VERIFY_RECOVER)!=0?true:false;
		else {
			
			log.error("Les informations de l'algorithme sont nulles, l'information sur l'algorithme est indisponible");
			throw new ExceptionObjet(ExceptionProgrammeExemple.TYPE_OBJET_ALGORITHME, "Linformation sur l'algorithme est indisponible");
			
		}

	}
	
	/**
	 * L'algorithme peut-il être utilisé pour générer une clé secrète?
	 * @return Réponse
	 * @throws ExceptionObjet 
	 */
	public boolean isGenereCleSecrete() throws ExceptionObjet {
		
		if(info != null)
			return (info.flags & PKCS11Constants.CKF_GENERATE)!=0?true:false;
		else {
			
			log.error("Les informations de l'algorithme sont nulles, l'information sur l'algorithme est indisponible");
			throw new ExceptionObjet(ExceptionProgrammeExemple.TYPE_OBJET_ALGORITHME, "Linformation sur l'algorithme est indisponible");
			
		}
		
	}
	
	/**
	 * L'algorithme peut-il être utilisé pour générer une paire de clés (publique/privée)?
	 * @return Réponse
	 * @throws ExceptionObjet 
	 */
	public boolean isGenerePaireCles() throws ExceptionObjet {
		
		if(info != null)
			return (info.flags & PKCS11Constants.CKF_GENERATE_KEY_PAIR)!=0?true:false;
		else {
			
			log.error("Les informations de l'algorithme sont nulles, l'information sur l'algorithme est indisponible");
			throw new ExceptionObjet(ExceptionProgrammeExemple.TYPE_OBJET_ALGORITHME, "Linformation sur l'algorithme est indisponible");
			
		}
		
	}
	
	/**
	 * L'algorithme est-il directement appliqué sur la carte?
	 * @return Réponse
	 * @throws ExceptionObjet 
	 */
	public boolean isChiffreCle() throws ExceptionObjet {
		
		if(info != null)
			return (info.flags & PKCS11Constants.CKF_WRAP)!=0?true:false;
		else {
			
			log.error("Les informations de l'algorithme sont nulles, l'information sur l'algorithme est indisponible");
			throw new ExceptionObjet(ExceptionProgrammeExemple.TYPE_OBJET_ALGORITHME, "Linformation sur l'algorithme est indisponible");
			
		}
		
	}
	
	/**
	 * L'algorithme peut-il être utilisé pour déchiffrer une clé?
	 * @return Réponse
	 * @throws ExceptionObjet 
	 */
	public boolean isDechiffreCle() throws ExceptionObjet {
		
		if(info != null)
			return (info.flags & PKCS11Constants.CKF_UNWRAP)!=0?true:false;
		else {
			
			log.error("Les informations de l'algorithme sont nulles, l'information sur l'algorithme est indisponible");
			throw new ExceptionObjet(ExceptionProgrammeExemple.TYPE_OBJET_ALGORITHME, "Linformation sur l'algorithme est indisponible");
			
		}
		
	}
	
	/**
	 * L'algorithme peut-il être utilisé pour dériver une clé depuis une autre clé?
	 * @return Réponse
	 * @throws ExceptionObjet 
	 */
	public boolean isDeriveCle() throws ExceptionObjet {
		
		if(info != null)
			return (info.flags & PKCS11Constants.CKF_DERIVE)!=0?true:false;
		else {
			
			log.error("Les informations de l'algorithme sont nulles, l'information sur l'algorithme est indisponible");
			throw new ExceptionObjet(ExceptionProgrammeExemple.TYPE_OBJET_ALGORITHME, "Linformation sur l'algorithme est indisponible");
			
		}
		
	}
	
	@Override
	public String toString() {
		return getLibelle();
	}
	
}